package com.zzu.base.common.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.jackson.io.JacksonDeserializer;
import io.jsonwebtoken.jackson.io.JacksonSerializer;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Collections;
import java.util.List;

@Component
@Data
public class JwtUtil {
    // 全局统一的 ObjectMapper，确保序列化/反序列化配置一致
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    static {
        OBJECT_MAPPER.registerModule(new JavaTimeModule());
        // 关键配置：禁止将数字序列化为字符串（解决 Long 被转为 String 的问题）
    }

    // JWT过期时间（秒）
    private static Long EXPIRE_TIME;

    // JWT刷新时间（秒）
    private static Long REFRESH_TIME;

    // JWT签名密钥
    private static String SECRET;

    @Value("${jwt.expire.time}")
    public void setExpireTime(Long expireTime) {
        JwtUtil.EXPIRE_TIME = expireTime;
    }

    @Value("${jwt.refresh.time}")
    public void setRefreshTime(Long refreshTime) {
        JwtUtil.REFRESH_TIME = refreshTime;
    }

    @Value("${jwt.secret}")
    public void setSecret(String secret) {
        JwtUtil.SECRET = secret;
    }

    /**
     * 生成JWT（使用统一的JacksonSerializer）
     */
    public static String createJWT(String userName, List<String> roles, List<String> permissions) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis(); // 毫秒时间戳

        JwtBuilder builder = Jwts.builder()
                .claim("issuedAt", nowMillis)
                .claim("refreshTime", nowMillis + REFRESH_TIME * 1000)
                .claim("expireTime", nowMillis + EXPIRE_TIME * 1000)
                .claim("userName", userName)
                .claim("roles", roles)
                .claim("permissions", permissions)
                // 生成时指定Jackson序列化器
                .serializeToJsonWith(new JacksonSerializer<>(OBJECT_MAPPER))
                .signWith(secretKey, signatureAlgorithm);
        return builder.compact();
    }

    /**
     * 生成加密secretKey
     */
    public static SecretKey generalKey() {
        byte[] keyBytes = SECRET.getBytes(StandardCharsets.UTF_8);
        return new SecretKeySpec(keyBytes, SignatureAlgorithm.HS256.getJcaName());
    }

    /**
     * 解析JWT（使用统一的Jackson反序列化器）
     */
    public static Claims parseJWT(String jwt)  {
        SecretKey secretKey = generalKey();
        return Jwts.parserBuilder()
                // 解析时指定Jackson反序列化器（与生成时配置一致）
                .deserializeJsonWith(new JacksonDeserializer<>(OBJECT_MAPPER))
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(jwt)
                .getBody();
    }

    // ===================== 时间戳与LocalDateTime转换 =====================
    public static LocalDateTime getIssuedAt(Claims claims) {
        Long timestamp = claims.get("issuedAt", Long.class);
        return timestamp != null ? timestampToLocalDateTime(timestamp) : null;
    }

    public static LocalDateTime getRefreshTime(Claims claims) {
        Long timestamp = claims.get("refreshTime", Long.class);
        return timestamp != null ? timestampToLocalDateTime(timestamp) : null;
    }

    public static LocalDateTime getExpireTime(Claims claims) {
        Long timestamp = claims.get("expireTime", Long.class);
        return timestamp != null ? timestampToLocalDateTime(timestamp) : null;
    }

    private static LocalDateTime timestampToLocalDateTime(Long timestamp) {
        return Instant.ofEpochMilli(timestamp)
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
    }

    // ===================== 其他声明解析 =====================
    public static String getUserName(Claims claims) {
        return claims.get("userName", String.class);
    }

    @SuppressWarnings("unchecked")
    public static List<String> getRoles(Claims claims) {
        Object roles = claims.get("roles");
        return roles instanceof List<?> ? (List<String>) roles : Collections.emptyList();
    }

    @SuppressWarnings("unchecked")
    public static List<String> getPermissions(Claims claims) {
        Object permissions = claims.get("permissions");
        return permissions instanceof List<?> ? (List<String>) permissions : Collections.emptyList();
    }

    // ===================== 验证逻辑 =====================
    public static boolean isTokenExpired(Claims claims) {
        Long expireTime = claims.get("expireTime", Long.class);
        return expireTime != null && System.currentTimeMillis() > expireTime;
    }

    public static boolean isTokenNeedRefresh(Claims claims) {
        Long refreshTime = claims.get("refreshTime", Long.class);
        return refreshTime != null &&
                System.currentTimeMillis() > refreshTime &&
                !isTokenExpired(claims);
    }
}